/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
 
This file is part of Quake III Arena source code.
 
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
 
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// sv_bot.c

#include "server.h"
#include "../game/botlib.h"

typedef struct bot_debugpoly_s
{
    int inuse;
    int color;
    int numPoints;
    vec3_t points[128];
}
bot_debugpoly_t;

static bot_debugpoly_t *debugpolygons;
int bot_maxdebugpolys;

extern botlib_export_t	*botlib_export;
int	bot_enable;


/*
==================
SV_BotAllocateClient
==================
*/
int SV_BotAllocateClient(void)
{
    int			i;
    client_t	*cl;

    // find a client slot
    for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
    {
        if ( cl->state == CS_FREE )
        {
            break;
        }
    }

    if ( i == sv_maxclients->integer )
    {
        return -1;
    }

    cl->gentity = SV_GentityNum( i );
    cl->gentity->s.number = i;
    cl->state = CS_ACTIVE;
    cl->lastPacketTime = svs.time;
    cl->netchan.remoteAddress.type = NA_BOT;
    cl->rate = 16384;

    return i;
}

/*
==================
SV_BotFreeClient
==================
*/
void SV_BotFreeClient( unsigned int clientNum )
{
    client_t	*cl;

    if ( clientNum < 0 || clientNum >= sv_maxclients->integer )
    {
        Com_Error( ERR_DROP, "SV_BotFreeClient: bad clientNum: %i", clientNum );
    }
    cl = &svs.clients[clientNum];
    cl->state = CS_FREE;
    cl->name[0] = 0;
    if ( cl->gentity )
    {
        cl->gentity->r.svFlags &= ~SVF_BOT;
    }
}

/*
==================
BotDrawDebugPolygons
==================
*/
void BotDrawDebugPolygons(void (*drawPoly)(int color, int numPoints, float *points), int value)
{
    static cvar_t *bot_debug, *bot_groundonly, *bot_reachability, *bot_highlightarea;
    bot_debugpoly_t *poly;
    int i, parm0;

    if (!debugpolygons)
        return;
    //bot debugging
    if (!bot_debug) bot_debug = Cvar_Get("bot_debug", "0", 0);
    //
    if (bot_enable && bot_debug->integer)
    {
        //show reachabilities
        if (!bot_reachability) bot_reachability = Cvar_Get("bot_reachability", "0", 0);
        //show ground faces only
        if (!bot_groundonly) bot_groundonly = Cvar_Get("bot_groundonly", "1", 0);
        //get the hightlight area
        if (!bot_highlightarea) bot_highlightarea = Cvar_Get("bot_highlightarea", "0", 0);
        //
        parm0 = 0;
        if (svs.clients[0].lastUsercmd.buttons & BUTTON_ATTACK) parm0 |= 1;
        if (bot_reachability->integer) parm0 |= 2;
        if (bot_groundonly->integer) parm0 |= 4;
        botlib_export->BotLibVarSet("bot_highlightarea", bot_highlightarea->string);
        botlib_export->Test(parm0, NULL, svs.clients[0].gentity->r.currentOrigin,
                            svs.clients[0].gentity->r.currentAngles);
    } //end if
    //draw all debug polys
    for (i = 0; i < bot_maxdebugpolys; i++)
    {
        poly = &debugpolygons[i];
        if (!poly->inuse) continue;
        drawPoly(poly->color, poly->numPoints, (float *) poly->points);
        //Com_Printf("poly %i, numpoints = %d\n", i, poly->numPoints);
    }
}

/*
==================
BotImport_Print
==================
*/
void QDECL BotImport_Print(int type, char *fmt, ...)
{
    char str[2048];
    va_list ap;

    va_start(ap, fmt);
    vsprintf(str, fmt, ap);
    va_end(ap);

    switch(type)
    {
    case PRT_MESSAGE:
        {
            Com_Printf("%s", str);
            break;
        }
    case PRT_WARNING:
        {
            Com_Printf(S_COLOR_YELLOW "Warning: %s", str);
            break;
        }
    case PRT_ERROR:
        {
            Com_Printf(S_COLOR_RED "Error: %s", str);
            break;
        }
    case PRT_FATAL:
        {
            Com_Printf(S_COLOR_RED "Fatal: %s", str);
            break;
        }
    case PRT_EXIT:
        {
            Com_Error(ERR_DROP, S_COLOR_RED "Exit: %s", str);
            break;
        }
    default:
        {
            Com_Printf("unknown print type\n");
            break;
        }
    }
}

/*
==================
BotImport_Trace
==================
*/
void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)
{
    trace_t trace;

    SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse);
    //copy the trace information
    bsptrace->allsolid = trace.allsolid;
    bsptrace->startsolid = trace.startsolid;
    bsptrace->fraction = trace.fraction;
    VectorCopy(trace.endpos, bsptrace->endpos);
    bsptrace->plane.dist = trace.plane.dist;
    VectorCopy(trace.plane.normal, bsptrace->plane.normal);
    bsptrace->plane.signbits = trace.plane.signbits;
    bsptrace->plane.type = trace.plane.type;
    bsptrace->surface.value = trace.surfaceFlags;
    bsptrace->ent = trace.entityNum;
    bsptrace->exp_dist = 0;
    bsptrace->sidenum = 0;
    bsptrace->contents = 0;
}

/*
==================
BotImport_EntityTrace
==================
*/
void BotImport_EntityTrace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask)
{
    trace_t trace;

    SV_ClipToEntity(&trace, start, mins, maxs, end, entnum, contentmask, qfalse);
    //copy the trace information
    bsptrace->allsolid = trace.allsolid;
    bsptrace->startsolid = trace.startsolid;
    bsptrace->fraction = trace.fraction;
    VectorCopy(trace.endpos, bsptrace->endpos);
    bsptrace->plane.dist = trace.plane.dist;
    VectorCopy(trace.plane.normal, bsptrace->plane.normal);
    bsptrace->plane.signbits = trace.plane.signbits;
    bsptrace->plane.type = trace.plane.type;
    bsptrace->surface.value = trace.surfaceFlags;
    bsptrace->ent = trace.entityNum;
    bsptrace->exp_dist = 0;
    bsptrace->sidenum = 0;
    bsptrace->contents = 0;
}


/*
==================
BotImport_PointContents
==================
*/
int BotImport_PointContents(vec3_t point)
{
    return SV_PointContents(point, -1);
}

/*
==================
BotImport_inPVS
==================
*/
int BotImport_inPVS(vec3_t p1, vec3_t p2)
{
    return SV_inPVS (p1, p2);
}

/*
==================
BotImport_BSPEntityData
==================
*/
char *BotImport_BSPEntityData(void)
{
    return CM_EntityString();
}

/*
==================
BotImport_BSPModelMinsMaxsOrigin
==================
*/
void BotImport_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin)
{
    clipHandle_t h;
    vec3_t mins, maxs;
    float max;
    int	i;

    h = CM_InlineModel(modelnum);
    CM_ModelBounds(h, mins, maxs);
    //if the model is rotated
    if ((angles[0] || angles[1] || angles[2]))
    {
        // expand for rotation

        max = RadiusFromBounds(mins, maxs);
        for (i = 0; i < 3; i++)
        {
            mins[i] = -max;
            maxs[i] = max;
        }
    }
    if (outmins) VectorCopy(mins, outmins);
    if (outmaxs) VectorCopy(maxs, outmaxs);
    if (origin) VectorClear(origin);
}

/*
==================
BotImport_GetMemory
==================
*/
void *BotImport_GetMemory(int size)
{
    void *ptr;

    ptr = Z_TagMalloc( size, TAG_BOTLIB );
    return ptr;
}

/*
==================
BotImport_FreeMemory
==================
*/
void BotImport_FreeMemory(void *ptr)
{
    Z_Free(ptr);
}

/*
=================
BotImport_HunkAlloc
=================
*/
void *BotImport_HunkAlloc( int size )
{
    if( Hunk_CheckMark() )
    {
        Com_Error( ERR_DROP, "SV_Bot_HunkAlloc: Alloc with marks already set\n" );
    }
    return Hunk_Alloc( size, h_high );
}

/*
==================
BotImport_DebugPolygonCreate
==================
*/
int BotImport_DebugPolygonCreate(int color, int numPoints, vec3_t *points)
{
    bot_debugpoly_t *poly;
    int i;

    if (!debugpolygons)
        return 0;

    for (i = 1; i < bot_maxdebugpolys; i++)
    {
        if (!debugpolygons[i].inuse)
            break;
    }
    if (i >= bot_maxdebugpolys)
        return 0;
    poly = &debugpolygons[i];
    poly->inuse = qtrue;
    poly->color = color;
    poly->numPoints = numPoints;
    Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
    //
    return i;
}

/*
==================
BotImport_DebugPolygonShow
==================
*/
void BotImport_DebugPolygonShow(int id, int color, int numPoints, vec3_t *points)
{
    bot_debugpoly_t *poly;

    if (!debugpolygons) return;
    poly = &debugpolygons[id];
    poly->inuse = qtrue;
    poly->color = color;
    poly->numPoints = numPoints;
    Com_Memcpy(poly->points, points, numPoints * sizeof(vec3_t));
}

/*
==================
BotImport_DebugPolygonDelete
==================
*/
void BotImport_DebugPolygonDelete(int id)
{
    if (!debugpolygons) return;
    debugpolygons[id].inuse = qfalse;
}

/*
==================
BotImport_DebugLineCreate
==================
*/
int BotImport_DebugLineCreate(void)
{
    vec3_t points[1];
    return BotImport_DebugPolygonCreate(0, 0, points);
}

/*
==================
BotImport_DebugLineDelete
==================
*/
void BotImport_DebugLineDelete(int line)
{
    BotImport_DebugPolygonDelete(line);
}

/*
==================
BotImport_DebugLineShow
==================
*/
void BotImport_DebugLineShow(int line, vec3_t start, vec3_t end, int color)
{
    vec3_t points[4], dir, cross, up = {0, 0, 1};
    float dot;

    VectorCopy(start, points[0]);
    VectorCopy(start, points[1]);
    //points[1][2] -= 2;
    VectorCopy(end, points[2]);
    //points[2][2] -= 2;
    VectorCopy(end, points[3]);


    VectorSubtract(end, start, dir);
    VectorNormalize(dir);
    dot = DotProduct(dir, up);
    if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
    else CrossProduct(dir, up, cross);

    VectorNormalize(cross);

    VectorMA(points[0], 2, cross, points[0]);
    VectorMA(points[1], -2, cross, points[1]);
    VectorMA(points[2], -2, cross, points[2]);
    VectorMA(points[3], 2, cross, points[3]);

    BotImport_DebugPolygonShow(line, color, 4, points);
}

/*
==================
SV_BotClientCommand
==================
*/
void BotClientCommand( int client, char *command )
{
    SV_ExecuteClientCommand( &svs.clients[client], command, qtrue );
}

/*
==================
SV_BotFrame
==================
*/
extern qboolean gamelib_started;
void SV_BotFrame( int time )
{
    if (!bot_enable)
        return;
    //NOTE: maybe the game is already shutdown
    if (!gamelib_started)
        return;

    BotAIStartFrame(time);
}

/*
===============
SV_BotLibSetup
===============
*/
int SV_BotLibSetup( void )
{
    if (!bot_enable)
    {
        return 0;
    }

    if ( !botlib_export )
    {
        Com_Printf( S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n" );
        return -1;
    }

    return botlib_export->BotLibSetup();
}

/*
===============
SV_ShutdownBotLib
 
Called when either the entire server is being killed, or
it is changing to a different game directory.
===============
*/
int SV_BotLibShutdown( void )
{

    if ( !botlib_export )
    {
        return -1;
    }

    return botlib_export->BotLibShutdown();
}

/*
==================
SV_BotInitCvars
==================
*/
void SV_BotInitCvars(void)
{

    Cvar_Get("bot_enable", "1", 0);						//enable the bot
    Cvar_Get("bot_developer", "0", CVAR_CHEAT);			//bot developer mode
    Cvar_Get("bot_debug", "0", CVAR_CHEAT);				//enable bot debugging
    Cvar_Get("bot_maxdebugpolys", "2", 0);				//maximum number of debug polys
    Cvar_Get("bot_groundonly", "1", 0);					//only show ground faces of areas
    Cvar_Get("bot_reachability", "0", 0);				//show all reachabilities to other areas
    Cvar_Get("bot_visualizejumppads", "0", CVAR_CHEAT);	//show jumppads
    Cvar_Get("bot_forceclustering", "0", 0);			//force cluster calculations
    Cvar_Get("bot_forcereachability", "0", 0);			//force reachability calculations
    Cvar_Get("bot_forcewrite", "0", 0);					//force writing aas file
    Cvar_Get("bot_aasoptimize", "0", 0);				//no aas file optimisation
    Cvar_Get("bot_saveroutingcache", "0", 0);			//save routing cache
    Cvar_Get("bot_thinktime", "100", CVAR_CHEAT);		//msec the bots thinks
    Cvar_Get("bot_reloadcharacters", "0", 0);			//reload the bot characters each time
    Cvar_Get("bot_testichat", "0", 0);					//test ichats
    Cvar_Get("bot_testrchat", "0", 0);					//test rchats
    Cvar_Get("bot_testsolid", "0", CVAR_CHEAT);			//test for solid areas
    Cvar_Get("bot_testclusters", "0", CVAR_CHEAT);		//test the AAS clusters
    Cvar_Get("bot_fastchat", "0", 0);					//fast chatting bots
    Cvar_Get("bot_nochat", "0", 0);						//disable chats
    Cvar_Get("bot_pause", "0", CVAR_CHEAT);				//pause the bots thinking
    Cvar_Get("bot_report", "0", CVAR_CHEAT);			//get a full report in ctf
    Cvar_Get("bot_grapple", "0", 0);					//enable grapple
    Cvar_Get("bot_rocketjump", "1", 0);					//enable rocket jumping
    Cvar_Get("bot_challenge", "0", 0);					//challenging bot
    Cvar_Get("bot_minplayers", "0", 0);					//minimum players in a team or the game
    Cvar_Get("bot_interbreedchar", "", CVAR_CHEAT);		//bot character used for interbreeding
    Cvar_Get("bot_interbreedbots", "10", CVAR_CHEAT);	//number of bots used for interbreeding
    Cvar_Get("bot_interbreedcycle", "20", CVAR_CHEAT);	//bot interbreeding cycle
    Cvar_Get("bot_interbreedwrite", "", CVAR_CHEAT);	//write interbreeded bots to this file
}

/*
==================
SV_BotInitBotLib
==================
*/
void SV_BotInitBotLib(void)
{
    botlib_import_t	botlib_import;

    if ( !Cvar_VariableValue("fs_restrict") && !Sys_CheckCD() )
    {
        Com_Error( ERR_NEED_CD, "Game CD not in drive" );
    }

    if (debugpolygons) Z_Free(debugpolygons);
    bot_maxdebugpolys = Cvar_VariableIntegerValue("bot_maxdebugpolys");
    debugpolygons = (bot_debugpoly_t *)Z_Malloc(sizeof(bot_debugpoly_t) * bot_maxdebugpolys);

    botlib_import.Print = BotImport_Print;
    botlib_import.Trace = BotImport_Trace;
    botlib_import.EntityTrace = BotImport_EntityTrace;
    botlib_import.PointContents = BotImport_PointContents;
    botlib_import.inPVS = BotImport_inPVS;
    botlib_import.BSPEntityData = BotImport_BSPEntityData;
    botlib_import.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin;
    botlib_import.BotClientCommand = BotClientCommand;

    //memory management
    botlib_import.GetMemory = BotImport_GetMemory;
    botlib_import.FreeMemory = BotImport_FreeMemory;
    botlib_import.AvailableMemory = Z_AvailableMemory;
    botlib_import.HunkAlloc = BotImport_HunkAlloc;

    // file system access
    botlib_import.FS_FOpenFile = FS_FOpenFileByMode;
    botlib_import.FS_Read = FS_Read2;
    botlib_import.FS_Write = FS_Write;
    botlib_import.FS_FCloseFile = FS_FCloseFile;
    botlib_import.FS_Seek = FS_Seek;

    //debug lines
    botlib_import.DebugLineCreate = BotImport_DebugLineCreate;
    botlib_import.DebugLineDelete = BotImport_DebugLineDelete;
    botlib_import.DebugLineShow = BotImport_DebugLineShow;

    //debug polygons
    botlib_import.DebugPolygonCreate = BotImport_DebugPolygonCreate;
    botlib_import.DebugPolygonDelete = BotImport_DebugPolygonDelete;

    botlib_export = (botlib_export_t *)GetBotLibAPI( BOTLIB_API_VERSION, &botlib_import );
    assert(botlib_export); 	// bk001129 - somehow we end up with a zero import.
}


//
//  * * * BOT AI CODE IS BELOW THIS POINT * * *
//

/*
==================
SV_BotGetConsoleMessage
==================
*/
int SV_BotGetConsoleMessage(unsigned int client, char *buf, unsigned int size )
{
    client_t	*cl;
    int			index;

    cl = &svs.clients[client];
    cl->lastPacketTime = svs.time;

    if ( cl->reliableAcknowledge == cl->reliableSequence )
    {
        return qfalse;
    }

    cl->reliableAcknowledge++;
    index = cl->reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 );

    if ( !cl->reliableCommands[index][0] )
    {
        return qfalse;
    }

    Q_strncpyz( buf, cl->reliableCommands[index], size );
    return qtrue;
}

#if 0
/*
==================
EntityInPVS
==================
*/
int EntityInPVS( int client, int entityNum )
{
    client_t			*cl;
    clientSnapshot_t	*frame;
    int					i;

    cl = &svs.clients[client];
    frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
    for ( i = 0; i < frame->num_entities; i++ )
    {
        if ( svs.snapshotEntities[(frame->first_entity + i) % svs.numSnapshotEntities].number == entityNum )
        {
            return qtrue;
        }
    }
    return qfalse;
}
#endif

/*
==================
SV_BotGetSnapshotEntity
==================
*/
int SV_BotGetSnapshotEntity( unsigned int client, unsigned int sequence )
{
    client_t			*cl;
    clientSnapshot_t	*frame;

    cl = &svs.clients[client];
    frame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
    if (sequence < 0 || sequence >= frame->num_entities)
    {
        return -1;
    }
    return svs.snapshotEntities[(frame->first_entity + sequence) % svs.numSnapshotEntities].number;
}

